package main.java.exercise;

import main.java.framework.Report;
import main.java.framework.Timer;
import main.java.framework.Verifier;

public class VerifierImplementation extends Verifier<InstanceImplementation, StudentSolutionImplementation, ResultImplementation> {

    @Override
    public ResultImplementation solveProblemUsingStudentSolution(InstanceImplementation instance, StudentSolutionImplementation studentSolution) {
        Timer timer = new Timer();
        timer.start();
        int[] ropeFragmentationNaive = new int[instance.getRopeLength() + 1];
        Timer timerNaive = new Timer();
        timerNaive.start();
        studentSolution.calculateRopeFragmentationNaive(instance.getRopeLength(), instance.getValuePerLength(), ropeFragmentationNaive);
        timerNaive.stop();
        int[] ropeFragmentationDynProg = new int[instance.getRopeLength() + 1];
        Timer timerDynProg = new Timer();
        timerDynProg.start();
        studentSolution.calculateRopeFragmentationDynProg(instance.getRopeLength(), instance.getValuePerLength(), ropeFragmentationDynProg);
        timerDynProg.stop();
        timer.stop();
        return new ResultImplementation(timer.getDuration(), instance.getRopeLength(), instance.getValuePerLength(), ropeFragmentationNaive, ropeFragmentationDynProg, timerNaive.getDuration(), timerDynProg.getDuration());
    }

    @Override
    public Report verifyResult(InstanceImplementation instance, ResultImplementation result) {
        int ropeLength = instance.getRopeLength();
        int[] valuePerLength = instance.getValuePerLength();
        boolean onlyTest = instance.getGroupName().equals("Rope Cutting Test");

        int[] ropeFragmentationNaive = result.getRopeFragmentationNaive();
        int calculatedLengthNaive = 0;
        int calculatedValueNaive = 0;
        for (int i = 1; i < ropeFragmentationNaive.length; i++) {
            calculatedLengthNaive += i * ropeFragmentationNaive[i];
            calculatedValueNaive += valuePerLength[i] * ropeFragmentationNaive[i];
        }
        if (calculatedLengthNaive != ropeLength) {
            if (calculatedLengthNaive == 0 && onlyTest) {
            } else {
                return new Report(false, "Error in instance " + instance.getNumber() + ": Accumulated length of rope pieces is " + calculatedLengthNaive + " and does not match rope length " + ropeLength + " in the naive implementation.");
            }
        }
        if (calculatedValueNaive != instance.getHighestPossibleValue()) {
            if (calculatedValueNaive == 0 && onlyTest) {
            } else {
                return new Report(false, "Error in instance " + instance.getNumber() + ": Accumulated value of rope pieces is " + calculatedValueNaive + " in the naive implementation but " + instance.getHighestPossibleValue() + " was expected.");
            }
        }

        int[] ropeFragmentationDynProg = result.getRopeFragmentationDynProg();
        int calculatedLengthDynProg = 0;
        int calculatedValueDynProg = 0;
        for (int i = 1; i < ropeFragmentationDynProg.length; i++) {
            calculatedLengthDynProg += i * ropeFragmentationDynProg[i];
            calculatedValueDynProg += valuePerLength[i] * ropeFragmentationDynProg[i];
        }
        if (calculatedLengthDynProg != ropeLength) {
            if (calculatedLengthDynProg == 0 && onlyTest) {
            } else {
                return new Report(false, "Error in instance " + instance.getNumber() + ": Accumulated length of rope pieces is " + calculatedLengthNaive + " and does not match rope length " + ropeLength + " in the dynamic programming implementation.");
            }
        }
        if (calculatedValueDynProg != instance.getHighestPossibleValue()) {
            if (calculatedValueDynProg == 0 && onlyTest) {
            } else {
                return new Report(false, "Error in instance " + instance.getNumber() + ": Accumulated value of rope pieces is " + calculatedValueNaive + " in the dynamic programming implementation but " + instance.getHighestPossibleValue() + " was expected.");
            }
        }

        return new Report(true, "");
    }
}
